
"use strict";

const Vector = require("./Vector");

/**
 * TODO !!!WARNING!!! !!!WARNING!!! !!!WARNING!!!
 *
 * !!!WARNING!!! !!!WARNING!!! !!!WARNING!!!
 *
 * This Function Is Base On The Condition That The Rectangle Has No Rotation.
 *
 * !!!WARNING!!! !!!WARNING!!! !!!WARNING!!!
 *
 *
 *     (0,0)----> x: right side is larger
 *     |
 *     v  y: down side is larger
 *
 *
 *         |      w       |       w     |
 *     -------------------|-------------------
 *       h |                            |  h
 *     ____|           (x,y)            |_____
 *         |              ^             |
 *       h |                            |  h
 *     -------------------|-------------------
 *         |      w       |       w     |
 *
 *
 * @param {Number} x  Center Point x And y Of The Rectangle
 * @param {Number} y
 * @param {Number} w  Half Width And Height
 * @param {Number} h
 * @constructor
 */
function Rectangle(x, y, w, h) {
    this.x = x;
    this.y = y;
    this.w = w; // Half-width
    this.h = h; // Half-height
}

module.exports = Rectangle;

/**
 * Create A Rectangle From A Circle
 * @param c Circle {"radian": Number, "position": Vector}
 * @returns {Rectangle}
 */
Rectangle.prototype.fromCell = function fromCell(c) {
    return new Rectangle(c.position.x, c.position.y, c.radian, c.radian);
};

// module.exports.fromCell = fromCell;

/**
 * Create A Rectangle From A Rectangle Bounds
 *
 * @param b
 * @returns {Rectangle}
 */
Rectangle.prototype.fromBounds = function fromBounds(b) {
    var w = b.right - b.left / 2,
        h = b.bottom - b.top / 2;

    return new Rectangle(
        b.left + w,
        b.top + h,
        w,
        h
    );
};

// module.exports.fromBounds = fromBounds;

/**
 * Get Bounds Of The Rectangle
 *
 * @returns {{left: number, right: *, top: number, bottom: *}}
 */
Rectangle.prototype.getBounds = function() {
    return {
        left: this.x - this.w,
        right: this.x + this.w,
        top: this.y - this.h,
        bottom: this.y + this.h
    };
};

/**
 * Get Point Vector Value Of Each Vertexes Of The Rectangle
 * @param {Number} quadrant
 * @returns {*}
 */
Rectangle.prototype.getBoundsPoint = function (quadrant) {
    var bounds = this.getBounds();

    //   point 2             point 1
    //       -----------------
    //       |               |
    //       |               |
    //       -----------------
    //   point 3             point 4

    var point;
    switch (quadrant) {
        case 1:
            point = new Vector(bounds.right, bounds.top);
            break;
        case 2:
            point = new Vector(bounds.left, bounds.top);
            break;
        case 3:
            point = new Vector(bounds.left, bounds.bottom);
            break;
        case 4:
            point = new Vector(bounds.right, bounds.bottom);
            break;
        default:
            point = false;
            break;
    }
    return point;
};

/**
 * Split The Rectangle Four Part
 *
 * Design For QuadTree
 *
 * @returns {Array}
 */
Rectangle.prototype.split = function() {
    // Split into 4 equally shaped rectangles
    var w2 = this.w / 2,
        h2 = this.h / 2,
        x = this.x,
        y = this.y;

    var ret = [];
    ret[0] = this;
    ret[1] = new Rectangle(x - w2, y - h2, w2, h2); // top-left
    ret[2] = new Rectangle(x + w2, y - h2, w2, h2); // top-right
    ret[3] = new Rectangle(x - w2, y + h2, w2, h2); // bottom-left
    ret[4] = new Rectangle(x + w2, y + h2, w2, h2); // bottom-right
    
    return ret;
};

/**
 * Calc If Two Rectangle Intersects
 *
 * @param {Rectangle} b
 * @returns {boolean}
 */
Rectangle.prototype.intersects = function(b) {
    var xa = this.x - this.w, ya = this.y - this.h, wa = this.w * 2, ha = this.h * 2,
        xb = b.x - b.w, yb = b.y - b.h, wb = b.w * 2, hb = b.h * 2;

    return xa <= xb + wb
        && xa + wa >= xb
        && ya <= yb + hb
        && ya + ha >= yb;
};

/**
 * Calc If Point Intersects This Rectangle
 *
 * @param {Vector} b
 * @returns {boolean}
 */
Rectangle.prototype.intersectsPoint = function(b) {
    var bounds = this.getBounds();
    var xb = b.x, yb = b.y;
    
    return bounds.top <= yb && yb <= bounds.bottom &&
        bounds.left <= xb && xb <= bounds.right;
};

/**
 * Stringify The Object
 * @returns {string}
 */
Rectangle.prototype.toString = function() {
    return "{ x: " + this.x + " y: " + this.y + " w: " + this.w + " h: " + this.h + " }";
};

/**
 * The Position Flag That A Point To A Rectangle
 * @param {Vector} p
 * @returns {number}
 */
Rectangle.prototype.pointPositionMarkFromRect = function (p) {
    var bounds = this.getBounds();

    //   LU2 |      U 10     |  RU1         // L: left
    // -----------------------------        // R: Right
    //       |               |              // U: Up Or Top
    //   L20 |   Rectangle 5 |  R40         // D: Down Or Bottom
    //       |               |              // Single (Not 5): Outer Point
    // -----------------------------        // Double : Outer Edge
    //   LD3 |       D30     |  RD4

    if (p.x > bounds.right && p.y > bounds.bottom) return 4;      // RD
    else if (p.x < bounds.left && p.y < bounds.top) return 2;    // LU
    else if (p.x < bounds.left && p.y > bounds.bottom) return 3; // LD
    else if (p.x > bounds.right && p.y < bounds.top) return 1;   // RU

    if (p.x <= bounds.right && p.x >= bounds.left
     && p.y <= bounds.bottom && p.y >= bounds.top) return 5; // Rectangle Internal

    if (p.x > bounds.right) return 40;        // R
    else if (p.x < bounds.left) return 20;   // L
    else if (p.y > bounds.bottom) return 30; // D        // 6 and 8 are exchanged, because:  double number / 2  ==> rotate 45 degree == equals quadrant
    else if (p.y < bounds.top) return 10;    // U

    return 0;
};

/**
 * If The Rectangle Intersects With A Circle
 *
 * @param c   Circle {"radian": Number, "position": Vector}
 */
Rectangle.prototype.intersectsCircle = function (c) {
    var posFlag = this.pointPositionMarkFromRect(c);

    var quadrant, quadPoint, relivatePoint, current;

    if (posFlag % 10 == 0) {
        // 圆正对着边, 算法是比较圆心到边的距离与圆的半径大学
        // 圆心到边的距离, 是圆心相对于对应的矩形顶点(及汇成顶点的两条边)的坐标系的坐标的一个维度(在矩形上下是y分量, 在矩形左右是x分量大小)


        // Calc The Point Value From The Coordinate System Constructed By Two Cross Edges Of The Rectangle
        quadrant = posFlag / 10;
        quadPoint = this.getBoundsPoint(quadrant);
        relivatePoint = c.position.clone().sub(quadPoint);

        // The Length From Point To The Nearest Edge Of The Rectangle
        if (quadrant == 1 || quadrant == 3) {
            current = Math.abs(relivatePoint.y);
        } else {
            current = Math.abs(relivatePoint.x);
        }

        return current <= c.radian;

    }

    if (posFlag != 5) {
        // 圆对着角, 算法是计算矩形顶点是否在圆内, 算法是比较该顶点到圆心的距离与圆的半径
        // 顶点到圆心的距离, 是圆心相对于该顶点的坐标系的x,y分量的勾股定理


        // Calc The Point Value From The Coordinate System Constructed By Two Cross Edges Of The Rectangle
        quadrant = posFlag;
        quadPoint = this.getBoundsPoint(quadrant);
        relivatePoint = c.position.clone().sub(quadPoint);

        // The Length From
        current = Math.sqrt(relivatePoint.x ^ 2 + relivatePoint.y ^ 2);

        return current <= c.radian;
    }

    // Circle Inside The Rectangle
    return false;
};